序、C++图像处理库
许多C++库可以用于读取和处理图像文件,常见的有SOIL2、Cimg、BoostGIL、Magick++等。
一、读取纹理文件
方法一:使用SOIL2:
//编写loadTexture函数:
GLuint loadTexture(const char * texImagePath)
{
GLuint textureID;
textureID = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
if (textureID == 0) cout << "couldn't find texture file" << texImagePath << endl;
return textureID;
}
其他方法:
有很多方法,比如用fopen()和fread()将数据从.bmp图像读入byte(unsigned char)数组。
二、创建纹理对象
方法一:使用SOIL2:
直接调用上面编写的loadTexture()
函数,返回值就是纹理对象(整型ID标识)。
方法二:手动创建并复制数据:
//创建纹理对象
GLuint textureID;
glGenTexture(1, &textureID);
//复制数据
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
三、纹理坐标
要将纹理应用于模型,需要为每个顶点指定纹理坐标。纹理坐标用于将模型上的点映射到纹理中的位置。(纹理中的像素被称为纹素(Texel)以便与屏幕的像素区分开)
四、纹理坐标载入缓冲区
纹理坐标单独通常设置一个顶点缓冲区,与三维坐标分开。
//...
float vertexTexCoords[24] = {
0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
0.0, 0.0, 1.0, 0.0, 0.5, 1.0,
};
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);//设置活跃缓冲区
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexTexCoords), vertexTexCoords, GL_STATIC_DRAW);//顶点数据加载进活跃缓冲区。
//...
五、应用纹理
渲染函数中把顶点属性传入着色器并激活纹理单元0:
void display(GLFWwindow * window, double currentTime)
{
//...
//把纹理坐标顶点属性传入着色器(location:1)
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
//激活纹理单元
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
//...
}
在着色器中声明采样器变量并关联纹理单元0,然后采样颜色:
//*******************Vert Shader**********************
#version 430
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texcoords;
out vec2 uv;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
//layout(binding = 0) uniform sampler2D samp;//顶点着色器一般用不到采样器
void main(void)
{
gl_Position = proj_matrix * mv_matrix * vec4(position, 1.0);
uv = texcoords;
}
//*******************Frag Shader**********************
#version 430
in vec2 uv;
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout(binding = 0) uniform sampler2D samp;
void main(void)
{
color = texture(samp, uv);
}
//****************************************************
六、多级渐远纹理(Mipmapping)
在信号处理中,采样不充分会产生走样现象。类似地,在纹理贴图采样中,当稀疏地采样高分辨率高细节图片时,会出现混乱随机的效果。如果纹理图像有重复图案,还有可能导致生成不同的图案。此时就需要使用多级渐远纹理贴图(Mipmapping)。
在编写的图片读取函数中添加:
GLuint loadTexture(const char * texImagePath)
{
//...
// MipMap
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
//...
}
※GL_TEXTURE_MIN_FILTER参数可以设置的的缩小方法:
- GL_NEAREST_MIPMAP_NEAREST:采样单个Mipmap的最近纹素
- GL_NEAREST_MIPMAP_LINEAR:线性过滤(两个Mipmap的最近纹素插值)
- GL_LINEAR_MIPMAP_NEAREST:双线性过滤(单个Mipmap的4个纹素插值)
- GL_LINEAR_MIPMAP_LINEAR:三线性过滤(两个Mipmap的4个纹素插值)
七、各向异性过滤(Anisotropy filtering)
GLuint loadTexture(const char * texImagePath)
{
//...
// Aniso
glBindTexture(GL_TEXTURE_2D, textureID);
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
{
GLfloat anisoset = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoset);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset);
}
//...
※glew的glewIsSupported()
可以查询显卡是否支持AF。
八、环绕和平铺(Clamp/Repeat)
Wrap方式:Clamp、Repeat:
GLuint loadTexture(const char * texImagePath)
{
glBindTexture(GL_TEXTURE_2D, textureID);
//...
// ClampOrRepeat
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//...
}
※glTexParameteri(GL_TEXTURE_2D, ??, ??)
的可选参数:
- GL_TEXTURE_WRAP_S/T, GL_REPEAT: 直接忽略整数部分,平铺方式。(默认)
- GL_TEXTURE_WRAP_S/T, GL_MIRRORED_REPEAT:平铺方式,但整数部分是奇数时坐标反转。
- GL_TEXTURE_WRAP_S/T, GL_CLAMP_TO_EDGE:限定在0~1之间。
Wrap方式:BorderBorder:
GLuint loadTexture(const char * texImagePath)
{
glBindTexture(GL_TEXTURE_2D, textureID);
//...
// Border
float color[4] = {0.0, 0.0, 0.0, 1.0};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color)
//...
}
※还可以用glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, <color>)
将外面的纹素设置为指定边框颜色。